Learning outcomes

  • Obtaining differential expression statistics for different contrasts
  • Using annotation databases to map between gene identifers
  • Construction and interpretation of common visualisations for RNA-seq
    • scatter plots
    • volcano plots
    • MA-plots
    • heatmaps

We can now have a list of genes ordered according to their evidence for being differentially-expressed. You should have saved a de_treated.rds object in the previous session.

de_treated <- readRDS("Robjects/de_treated.rds")
dds <- readRDS("Robjects/dds.rds")

If you don’t have it, you can download directly into RStudio using the code below:-

dir.create("Robjects/")
download.file("https://github.com/sheffield-bioinformatics-core/bms31004_2022/raw/main/Robjects/de_treated.rds",destfile = "Robjects/de_treated.rds")
download.file("https://github.com/sheffield-bioinformatics-core/bms31004_2022/raw/main/Robjects/dds.rds",destfile = "Robjects/dds.rds")


de_treated <- readRDS("Robjects/de_treated.rds")
dds <- readRDS("Robjects/dds.rds")
library(tidyverse)
library(DESeq2)

An overview of the results is to use the plotMA function. Each point on this plot represents and individual gene with the x- and y-axes being the overall expression level and magnitude of difference respectively. Significant genes are automatically highlighted, although at this stage we don’t have a convenient way of labeling the genes. The fanning effect at low expression levels is often seen due to high relative fold-change at low expression levels.

plotMA(de_treated)

It is also instructive to perform a “sanity” check and plot the sample-level counts for genes with high significance. This could highlight any other technical factors that we are not currently taking into account. The plot is not particularly attractive, but is a good quick diagnostic. If you pick genes with a large fold-change (and/or low p-value) you can verify the “direction” of the contrast and which sample group was used as a baseline.

results_treated <- results(de_treated, tidy=TRUE) %>% arrange(padj)
## this gene has a positive fold-change
plotCounts(dds,"ENSG00000158258",intgroup = "Treated")

## this gene has a negative fold-change
plotCounts(dds,"ENSG00000136999",intgroup = "Treated")

From the plots we can confirm that un-treated samples (annotated with N in the Treatment group) was used as a baseline; in other words a positive fold-change means a higher expression level in Treated samples. We will see shortly how to change the direction of the contrast.

If your study involves knocking-out a particular gene, or you have some positive controls that are known in advance, it would be a good idea to visualise their expression level with plotCounts.

Exercise

  • Re-run the DESeq function to find differentially-expressed genes between the TGF treated samples and CTRL. You will need to change the design that is used.
  • Write a csv file that contains results for the genes that have a p-value less than 0.05 and a log2 fold change more than 1, or less than -1.
    • HINT: So that we don’t overwrite our results so far, it may be convenient to create a new DESeqDataSet object for the new differential expression analysis. Check the colData to see which analyses can be made
  • Use the plotCounts function to visually-inspect the most statistically-significant gene identified
dds_condition<- dds
colData(dds_condition)
## You will need to change this line to choose the correct comparison
design(dds_condition) <- ~...

Changing the direction of the contrast

In this initial analysis DESeq2 has automatically decided which member of our sample groups to use as our baseline (CTR in this case) so that the log2 fold changes are reported with a positive value meaning higher expression in Treated. If we want to change this behaviour we can change the contrast argument in the results function

## This should give the same as the table above
results(de_treated, contrast=c("Treated","Y","N"))
## Changing the direction of the contrast
results(de_treated, contrast=c("Treated","N","Y"))

Analyses involving more than two groups

If we change to performing differential expression analysis on the condition variable then there are various contrasts that can be made; IR vs CTR, TGF vs CTR etc. When the results function is run, the table that is displayed is for the contrast TGF vs CTR. The resultsNames function can tell us which other contrasts we can access.

dds_condition<- dds
dds_condition$condition <- as.factor(stringr::str_to_upper(dds_condition$condition))
design(dds_condition) <- ~condition
de_condition <- DESeq(dds_condition)
resultsNames(de_condition)
[1] "Intercept"            "condition_IR_vs_CTR" 
[3] "condition_TGF_vs_CTR"

For a factor with more than 2 groups, the contrast argument can be used to change the output. Here, we compare the IR to CTR -treated groups.

results(de_condition, contrast = c("condition","IR","CTR"),tidy=TRUE)  %>%
arrange(padj) 

If we wanted to change the analysis to IR versus TGF we don’t have to re-run the analysis. The results function can be used to extract a different contrast.

results(de_condition, contrast = c("condition","IR","TGF"),tidy=TRUE) %>%
arrange(padj) 

Analysing a subset of the data

The tximport and DESeq2 packages make it quite convenient to import a set of RNA-seq counts into R. However, we don’t neccesarily have to use all the samples in our differential expression analysis.

For example, we might decide, based on the sample QC, that one of our samples should be removed as an outlier.

The dds object can be treated with the usual subsetting conventions as a count matrix (even though it is a more complex data type). Specifically we can define a set of column indices to subset the samples.

e.g. this will return a DESeqDataset using the first four samples from dds.

dds[,1:4]
class: DESeqDataSet 
dim: 57914 4 
metadata(1): version
assays(2): counts avgTxLength
rownames(57914): ENSG00000000003 ENSG00000000005 ...
  ENSG00000284747 ENSG00000284748
rowData names(0):
colnames(4): 1_CTR_BC_2 2_TGF_BC_4 3_IR_BC_5
  4_CTR_BC_6
colData names(5): Run condition Name Replicate
  Treated
samples_to_keep <- c("1_CTR_BC_2","2_TGF_BC_4","3_IR_BC_5",
                     "4_CTR_BC_6","5_TGF_BC_7", "6_IR_BC_12",
                     "7_CTR_BC_13","8_TGF_BC_14","9_IR_BC_15")
dds_final <- dds[,dds$Run %in% samples_to_keep]
## Proceed as usual
samples_to_remove <- "7_CTR_BC_13"
dds_final <- dds[,!dds$Run %in% samples_to_remove]

Or based on some criteria such as the number of reads

number_of_reads <- colSums(counts(dds)) / 1e6
dds_final <- dds[,number_of_reads > 35]

Finally, we might decide to analyse a specific biological group.

dds_treated <- dds[,dds$Treated == "Y"]

More-complex designs

The examples we have used so far have performed a differential expression analysis using a named column in the colData object. The DESeq2 package is capable of performing more complex analyses that can take multiple factors into consideration at the same time; so-called “multi-factor designs”

The use of such a design could be motivated by discovering sources of technical variation in our data that might obscure the biological differences we would like to compare. e.g.

In the example image above the main source of variation is the batch in which the samples were sequenced. A multi-factor analysis to compare the various conditions, but “correct” for differences in batch, would be as follows.

### Don't run this. It's just a code example
design(MY_DATA) <- ~ batch + condition

Likewise, if we have different treatments applied to difference cell-lines, but the main source of variation is the cell line (as in Weekly Exercise 4), the following could be used.

### Don't run this. It's just a code example
design(MY_DATA) <- ~cell_line + treatment

Adding annotation to the DESeq2 results

We would love to share this list with our collaborators, or search for our favourite gene in the results. However, the results are not very useful in there current form as each row is named according to an Ensembl identifier. Whilst gene symbols are problematic and can change over time, they are the names that are most recognisable and make the results easier to navigate.

There are a number of ways to add annotation, but we will demonstrate how to do this using the org.Hs.eg.db package. This package is one of several organism-level packages in Bioconductor that are re-built every 6 months. These packages are listed on the annotation section of the Bioconductor, and are installed in the same way as regular Bioconductor packages. An alternative approach is to use biomaRt, an interface to the BioMart resource. BioMart is much more comprehensive, but the organism packages fit better into the Bioconductor workflow.

### Only execute when you need to install the package
install.packages("BiocManager")
BiocManager::install("org.Hs.eg.db")
# For Human
BiocManager::install("org.Hs.eg.db")

The packages are larger in size that Bioconductor software pacakges, but essentially they are databases that can be used to make offline queries.

library(org.Hs.eg.db)

The following error may be seen loading this library on some versions of RStudio.

Show in New WindowClear OutputExpand/Collapse Output
Error: package or namespace load failed for ‘org.Hs.eg.db’: .onLoad failed in loadNamespace() for 'org.Hs.eg.db', details: call: l$contains error: $ operator is invalid for atomic vectors

A solution is presented in this forum post

options(connectionObserver = NULL)
library(org.Hs.eg.db)

First we need to decide what information we want. In order to see what we can extract we can run the columns function on the annotation database.

columns(org.Hs.eg.db)
 [1] "ACCNUM"       "ALIAS"        "ENSEMBL"     
 [4] "ENSEMBLPROT"  "ENSEMBLTRANS" "ENTREZID"    
 [7] "ENZYME"       "EVIDENCE"     "EVIDENCEALL" 
[10] "GENENAME"     "GENETYPE"     "GO"          
[13] "GOALL"        "IPI"          "MAP"         
[16] "OMIM"         "ONTOLOGY"     "ONTOLOGYALL" 
[19] "PATH"         "PFAM"         "PMID"        
[22] "PROSITE"      "REFSEQ"       "SYMBOL"      
[25] "UCSCKG"       "UNIPROT"     

We are going to filter the database by a key or set of keys in order to extract the information we want. Valid names for the key can be retrieved with the keytypes function.

keytypes(org.Hs.eg.db)
 [1] "ACCNUM"       "ALIAS"        "ENSEMBL"     
 [4] "ENSEMBLPROT"  "ENSEMBLTRANS" "ENTREZID"    
 [7] "ENZYME"       "EVIDENCE"     "EVIDENCEALL" 
[10] "GENENAME"     "GENETYPE"     "GO"          
[13] "GOALL"        "IPI"          "MAP"         
[16] "OMIM"         "ONTOLOGY"     "ONTOLOGYALL" 
[19] "PATH"         "PFAM"         "PMID"        
[22] "PROSITE"      "REFSEQ"       "SYMBOL"      
[25] "UCSCKG"       "UNIPROT"     

We should see ENSEMBL, which is the type of key we are going to use in this case. If we are unsure what values are acceptable for the key, we can check what keys are valid with keys

keys(org.Hs.eg.db, keytype="ENSEMBL")[1:10]
 [1] "ENSG00000121410" "ENSG00000175899" "ENSG00000256069"
 [4] "ENSG00000171428" "ENSG00000156006" "ENSG00000196136"
 [7] "ENSG00000114771" "ENSG00000127837" "ENSG00000129673"
[10] "ENSG00000090861"

For the top gene in our analysis the call to the function would be:-

select(org.Hs.eg.db, keys="ENSG00000158258",
       keytype = "ENSEMBL",columns=c("SYMBOL","GENENAME")
)

Unfortunately, the authors of dplyr and AnnotationDbi have both decided to use the name select in their packages. To avoid confusion and errors, the following code is sometimes used to explictly tell RStudio to use the select function from AnnotationDbi.

AnnotationDbi::select(org.Hs.eg.db, keys="ENSG00000158258",keytype = "ENSEMBL",columns=c("SYMBOL","GENENAME"))

To annotate our results, we definitely want gene symbols and perhaps the full gene name. Let’s build up our annotation information into a new data frame using the select function. The keys argument to select is a vector of valid identifiers. The names of all Ensembl IDs used in the analysis can be found as the rownames of our DESeq2 object.

## Get the names of all genes used in the analysis
all_ids <- rownames(dds)
anno <- AnnotationDbi::select(org.Hs.eg.db,keys=all_ids,
              columns=c("SYMBOL","GENENAME"),
              keytype="ENSEMBL")
# Have a look at the annotation
head(anno)

However, we have a problem that the resulting data frame has more rows than our results table. This is due to the one-to-many relationships that often occur when mapping between various identifiers.

dim(anno)
[1] 58140     3
dim(results_treated)
[1] 57914     7

Such duplicated entries can be identified using the duplicated function. This will be either TRUE or FALSE if a particular entry is repeated again in a column. Since we are interested in the entries that are not repeated we an use the R syntax ! which will reverse a set of logical values.

df <- data.frame(x = 1:10, y = c(LETTERS[1:6],LETTERS[1:4]))
df
filter(df, duplicated(y))
filter(df, !duplicated(y))

Fortunately, there are not too many duplicated entries so hopefully we won’t lose too much information if we discard the entries that are duplicates The first occurrence of the duplicated ID will still be included in the table.

anno <- AnnotationDbi::select(org.Hs.eg.db,keys=all_ids,
            columns=c("ENSEMBL","SYMBOL","GENENAME","ENTREZID"),
            keytype="ENSEMBL") %>% 
filter(!duplicated(ENSEMBL))
dim(anno)
[1] 57914     4

We can now join the annotation information to the results data frame. This can be done because both data frame contains the same set of identifiers. However, the column names are different so need to tell R which columns contain the same data (row and ENSEMBL respectively).

results_annotated <- left_join(results_treated, anno,by=c("row"="ENSEMBL")) 
head(results_annotated)

We can save the results table using the write.csv function, which writes the results out to a csv file that you can open in excel.

dir.create("de_analysis",showWarnings = FALSE)
write.csv(results_annotated,file="out_data/DESeq_treatment_Y_vs_N_annotated.csv",row.names=FALSE)

Exercise

  • Join the annotation table to your results from the DESeq analysis of TGF vs CTR. Save the resulting data frame as a csv file. e.g. (out_data/DESeq_condition_TGF_vs_CTR_annotated.csv)
  • The publication gives examples of COL1A1, COL1A2 and COL3A1 as genes that are up-regulated in TGF-treated samples vs controls (Figure 6C). Use your data to verify this by
      1. plotting the counts for these genes
      1. extracting their p-values

Visualisations incorporating gene names

The volcano plot

Similar to the MA-plot we saw earlier, the volcano plot is often used to display the results of a differential expression analysis. The x-axis shows the log\(_2\) fold-change and the y-axis is a measure of significance - typically a -log\(_{10}\) transformed version of the p-value.

We would not be able to visualise the p-values without any transformation (they would be squashed on the bottom of the y-axis). By using a log\(_{10}\) transformation we get quantities that are easier to visualise. Applying a minus sign to the result places more significant p-values at the top of the plot.

p <- c(0.05,0.01,0.0001)
log10(p)
[1] -1.30103 -2.00000 -4.00000
-log10(p)
[1] 1.30103 2.00000 4.00000

All the data we need for the plot is included in our annotated results table.

## We manually set the x-limit in this case
ggplot(results_annotated, aes(x = log2FoldChange, y = -log10(padj))) + geom_point() + xlim(-5,5)

To make the plot more useful we can label individual points. The general way of adding of adding labels is by using geom_text in conjunction with the label aesthetic. However, it is not very sensible to blindly add the geom_text function. We use the SYMBOL column from the results table as these are recognisable gene names.

ggplot(results_annotated,aes(x = log2FoldChange, y = -log10(padj),label=SYMBOL)) + geom_point() + geom_text()

The problem is that ggplot tries to label every gene that was tested in the analysis; which is clearly not very sensible.

Let’s say that we only want to label the top 20 most significant genes. The label we would want to give these genes is the gene name (SYMBOL), whereas the reminder of the genes should have no label. We can assign a set of labels based on where the genes are ranked in the gene list by using an ifelse statement in R.

With ifelse we can assign labels based on a logical condition. If the condition is TRUE then one label is assigned; if FALSE an alternative is assigned. In our case we want the label to be the gene symbol if the gene ranks less than 20 in. If not, the label is the empty string.

mutate(results_annotated, Rank = 1:n()) %>% 
  mutate(Label = ifelse(Rank <= 20, SYMBOL, ""))

We can now re-run the plot but using the newly created labels

mutate(results_annotated, Rank = 1:n()) %>% 
  mutate(Label = ifelse(Rank <= 20, SYMBOL, "")) %>% 
## notice how we set the label argument to be Label rather than SYMBOL
ggplot(aes(x = log2FoldChange, y = -log10(padj),label=Label)) + geom_point() + geom_text(col="blue")

A nicer way of displaying the names is provided by the ggrepel package

if(!require(ggrepel)) install.packages("ggrepel")
library(ggrepel)
mutate(results_annotated, Rank = 1:n()) %>% 
  mutate(Label = ifelse(Rank <= 20, SYMBOL, "")) %>% 
ggplot(aes(x = log2FoldChange, y = -log10(padj),label=Label)) + geom_point() + geom_text_repel(col="blue")

Making a heatmap from the differential expression results

A heatmap is a very popular method for visualising gene expression dataset. It shows the expression levels of a set of genes, and how those genes separate the samples in the dataset into groups. Like the PCA plot we used previously, it is an unsupervised method whereby known biological groups are added after the plot has been created.

However, we don’t use the whole expression matrix to construct the heatmap; attempting to do this will quickly cause R to crash. Moreoever, the plot wouldn’t be particularly informative as most genes are not expected to change between biological conditions, or won’t even be expressed.

We often use heatmaps to show a very small set of genes. The most statistically-significant genes are a popular choice as they should discriminate well between biological groups.

The first step is to identify the Ensembl IDs of the most significant genes

de_ids <- results_annotated %>% 
  arrange(padj) %>% 
  dplyr::slice(1:10) %>% 
  pull("row")
de_ids
 [1] "ENSG00000158258" "ENSG00000166396" "ENSG00000136999"
 [4] "ENSG00000115594" "ENSG00000019991" "ENSG00000070193"
 [7] "ENSG00000168398" "ENSG00000149256" "ENSG00000119508"
[10] "ENSG00000136960"

The heatmap tool we are going to use requires a count matrix as input, and our count matrices have Ensembl IDs as row identifiers. As with the boxplots and PCA plots from before this visualisation works best for transformed counts.

library(pheatmap)
vsd <- vst(dds)
heatmap_data <- assay(vsd)[de_ids,]
pheatmap(heatmap_data)

A dendrogram is used to show the relationship between samples; whereby samples that have a short “branch” joining them being similar to each other. However, samples being next to each other on the plot does not imply that they are closely related.

The code to generate the heatmap can be modified to include useful sample annotations. The meta data for this dataset is stored in dds but must undergone a bit of modification before we can include it.

## the meta data must be a data frame
samp_anno <- data.frame(colData(dds))
## the rownames must match the columns of the data being plotted
rownames(samp_anno) <- dds$Run
pheatmap(heatmap_data,
         annotation_col = samp_anno)

We can do some tidying-up as not all the columns in our annotation data frame need to be displayed

samp_anno <- dplyr::select(samp_anno, Treated, Replicate) %>% 
  mutate(Replicate = as.factor(Replicate))
pheatmap(heatmap_data,
         annotation_col = samp_anno)

Exercise:

  • Make another heatmap of the dataset, but this time using the 30 genes with the largest absolute fold-change
  • The cells in the heatmap are often scaled at a gene-level according to the change from mean rather than absolute value. Look at the help for pheatmap to see how to change the heatmap
  • It would be more informative to have the rows labeled according to the SYMBOL rather than Ensembl ID. Look at the help for pheatmap; which argument do you need to change? Change the heatmap so that SYMBOLs are shown for each row.

APPENDIX: Full DESeq workflow

The median of ratios normalisation method is employed in DESeq2 to account for sequencing depth and RNA composition. Let’s go through a short worked example (courtesy of https://hbctraining.github.io/DGE_workshop/lessons/02_DGE_count_normalization.html) to explain the process.

## create a small example matrix of "counts"
test_data <- matrix(c(1489,22,793,76,521,906,13,410,42,1196),nrow=5)
rownames(test_data) <- c("EF2A","ABCD1","MEFV","BAG1","MOV10")
colnames(test_data) <- c("SampleA","SampleB")
test_data
      SampleA SampleB
EF2A     1489     906
ABCD1      22      13
MEFV      793     410
BAG1       76      42
MOV10     521    1196

Firstly, an “average” or reference sample is created that represents the counts on a typical sample in the dataset. The geometric mean is used rather than the arithmetic mean. In other words the individual counts are multiplied rather than suHsed and the measure should be more robust to outliers.

psuedo_ref <- sqrt(rowProds(test_data))
psuedo_ref
[1] 1161.47923   16.91153  570.20172   56.49779  789.37697

A ratios of sample to “psuedo reference” are then calculated for each gene. We are assuming that most genes are not changing dramatically, so this ratio should be somewhere around 1.

test_data/psuedo_ref
        SampleA   SampleB
EF2A  1.2819859 0.7800398
ABCD1 1.3008873 0.7687061
MEFV  1.3907359 0.7190438
BAG1  1.3451854 0.7433919
MOV10 0.6600142 1.5151189

DESeq2 defines size factors as being the median of these ratios for each sample (median is used so any outlier genes will not affect the normalisation).

norm_factors <- colMedians(test_data/psuedo_ref)
norm_factors
[1] 1.3008873 0.7687061

Individual samples can then normalised by dividing the count for each gene by the corresponding normalization factor.

test_data[,1] / norm_factors[1]
      EF2A      ABCD1       MEFV       BAG1      MOV10 
1144.60340   16.91153  609.58395   58.42166  400.49589 

and for the second sample…

test_data[,2] / norm_factors[2]
      EF2A      ABCD1       MEFV       BAG1      MOV10 
1178.60387   16.91153  533.36378   54.63727 1555.86118 

The size factors for each sample in our dataset can be calculated using the estimateSizeFactorsForMatrix function.

sf <- estimateSizeFactorsForMatrix(assay(dds))
sf
 1_CTR_BC_2  2_TGF_BC_4   3_IR_BC_5  4_CTR_BC_6  5_TGF_BC_7 
  1.0549852   1.1812947   0.8928435   1.0990482   1.0518069 
 6_IR_BC_12 7_CTR_BC_13 8_TGF_BC_14  9_IR_BC_15 
  0.8626010   0.9491286   1.0643407   0.9783918 

The estimation of these factors can also take gene-lengths into account, and this is implemented in the estimateSizeFactors function. Extra normalization factor data is added to the dds object.

dds <- estimateSizeFactors(dds)
dds

In preparation for differential expression DESeq2 also need a reliable estimate of the variability of each gene; which it calls dispersion.

dds <- estimateDispersions(dds)
dds

A statistical test can then be applied. As the data are count-based and not normally-distributed a t-test would not be appropriate. Most tests are based on a Poisson or negative-binomial distribution; negative binomial in the case of DESeq2. Although you might not be familiar with the negative binomial, the results should be in a familiar form with fold-changes and p-values for each gene.

dds <- nbinomWaldTest(dds)

It may seem like there is a lot to remember, but fortunately there is one convenient function that will apply the three steps. The messages printed serve as reminders of the steps included.

LS0tCnRpdGxlOiAiQk1TMzEwMDQgQmlvaW5mb3JtYXRpY3MgZm9yIEJpb21lZGljYWwgU2NpZW5jZSAtIFdlZWsgNSIKYXV0aG9yOiAiTW9kdWxlIENvb3JkaW5hdG9yIE1hcmsgRHVubmluZyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgY3NzOiBzdHlsZXNoZWV0cy9zdHlsZXMuY3NzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSkKbGlicmFyeShkcGx5cikKCmBgYAoKCgojIExlYXJuaW5nIG91dGNvbWVzCgotIE9idGFpbmluZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBzdGF0aXN0aWNzIGZvciBkaWZmZXJlbnQgY29udHJhc3RzCi0gVXNpbmcgYW5ub3RhdGlvbiBkYXRhYmFzZXMgdG8gbWFwIGJldHdlZW4gZ2VuZSBpZGVudGlmZXJzCi0gQ29uc3RydWN0aW9uIGFuZCBpbnRlcnByZXRhdGlvbiBvZiBjb21tb24gdmlzdWFsaXNhdGlvbnMgZm9yIFJOQS1zZXEKICAgICsgc2NhdHRlciBwbG90cwogICAgKyB2b2xjYW5vIHBsb3RzCiAgICArIE1BLXBsb3RzIAogICAgKyBoZWF0bWFwcwoKV2UgY2FuIG5vdyBoYXZlIGEgbGlzdCBvZiBnZW5lcyBvcmRlcmVkIGFjY29yZGluZyB0byB0aGVpciBldmlkZW5jZSBmb3IgYmVpbmcgZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkLiBZb3Ugc2hvdWxkIGhhdmUgc2F2ZWQgYSBgZGVfdHJlYXRlZC5yZHNgIG9iamVjdCBpbiB0aGUgcHJldmlvdXMgc2Vzc2lvbi4KCmBgYHtyfQpkZV90cmVhdGVkIDwtIHJlYWRSRFMoIlJvYmplY3RzL2RlX3RyZWF0ZWQucmRzIikKZGRzIDwtIHJlYWRSRFMoIlJvYmplY3RzL2Rkcy5yZHMiKQpgYGAKCklmIHlvdSBkb24ndCBoYXZlIGl0LCB5b3UgY2FuIGRvd25sb2FkIGRpcmVjdGx5IGludG8gUlN0dWRpbyB1c2luZyB0aGUgY29kZSBiZWxvdzotCgpgYGB7ciBldmFsPUZBTFNFfSAKZGlyLmNyZWF0ZSgiUm9iamVjdHMvIikKZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9naXRodWIuY29tL3NoZWZmaWVsZC1iaW9pbmZvcm1hdGljcy1jb3JlL2JtczMxMDA0XzIwMjIvcmF3L21haW4vUm9iamVjdHMvZGVfdHJlYXRlZC5yZHMiLGRlc3RmaWxlID0gIlJvYmplY3RzL2RlX3RyZWF0ZWQucmRzIikKZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9naXRodWIuY29tL3NoZWZmaWVsZC1iaW9pbmZvcm1hdGljcy1jb3JlL2JtczMxMDA0XzIwMjIvcmF3L21haW4vUm9iamVjdHMvZGRzLnJkcyIsZGVzdGZpbGUgPSAiUm9iamVjdHMvZGRzLnJkcyIpCgoKZGVfdHJlYXRlZCA8LSByZWFkUkRTKCJSb2JqZWN0cy9kZV90cmVhdGVkLnJkcyIpCmRkcyA8LSByZWFkUkRTKCJSb2JqZWN0cy9kZHMucmRzIikKYGBgCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoREVTZXEyKQpgYGAKCgpBbiBvdmVydmlldyBvZiB0aGUgcmVzdWx0cyBpcyB0byB1c2UgdGhlIGBwbG90TUFgIGZ1bmN0aW9uLiBFYWNoIHBvaW50IG9uIHRoaXMgcGxvdCByZXByZXNlbnRzIGFuZCBpbmRpdmlkdWFsIGdlbmUgd2l0aCB0aGUgeC0gYW5kIHktYXhlcyBiZWluZyB0aGUgb3ZlcmFsbCBleHByZXNzaW9uIGxldmVsIGFuZCBtYWduaXR1ZGUgb2YgZGlmZmVyZW5jZSByZXNwZWN0aXZlbHkuIFNpZ25pZmljYW50IGdlbmVzIGFyZSBhdXRvbWF0aWNhbGx5IGhpZ2hsaWdodGVkLCBhbHRob3VnaCBhdCB0aGlzIHN0YWdlIHdlIGRvbid0IGhhdmUgYSBjb252ZW5pZW50IHdheSBvZiBsYWJlbGluZyB0aGUgZ2VuZXMuIFRoZSBmYW5uaW5nIGVmZmVjdCBhdCBsb3cgZXhwcmVzc2lvbiBsZXZlbHMgaXMgb2Z0ZW4gc2VlbiBkdWUgdG8gaGlnaCByZWxhdGl2ZSBmb2xkLWNoYW5nZSBhdCBsb3cgZXhwcmVzc2lvbiBsZXZlbHMuIAoKYGBge3J9CnBsb3RNQShkZV90cmVhdGVkKQpgYGAKCkl0IGlzIGFsc28gaW5zdHJ1Y3RpdmUgdG8gcGVyZm9ybSBhICJzYW5pdHkiIGNoZWNrIGFuZCBwbG90IHRoZSBzYW1wbGUtbGV2ZWwgY291bnRzIGZvciBnZW5lcyB3aXRoIGhpZ2ggc2lnbmlmaWNhbmNlLiBUaGlzIGNvdWxkIGhpZ2hsaWdodCBhbnkgb3RoZXIgdGVjaG5pY2FsIGZhY3RvcnMgdGhhdCB3ZSBhcmUgbm90IGN1cnJlbnRseSB0YWtpbmcgaW50byBhY2NvdW50LiBUaGUgcGxvdCBpcyBub3QgcGFydGljdWxhcmx5IGF0dHJhY3RpdmUsIGJ1dCBpcyBhIGdvb2QgcXVpY2sgZGlhZ25vc3RpYy4gSWYgeW91IHBpY2sgZ2VuZXMgd2l0aCBhIGxhcmdlIGZvbGQtY2hhbmdlIChhbmQvb3IgbG93IHAtdmFsdWUpIHlvdSBjYW4gdmVyaWZ5IHRoZSAiZGlyZWN0aW9uIiBvZiB0aGUgY29udHJhc3QgYW5kIHdoaWNoIHNhbXBsZSBncm91cCB3YXMgdXNlZCBhcyBhIGJhc2VsaW5lLgoKYGBge3J9CnJlc3VsdHNfdHJlYXRlZCA8LSByZXN1bHRzKGRlX3RyZWF0ZWQsIHRpZHk9VFJVRSkgJT4lIGFycmFuZ2UocGFkaikKYGBgCgoKYGBge3J9CiMjIHRoaXMgZ2VuZSBoYXMgYSBwb3NpdGl2ZSBmb2xkLWNoYW5nZQpwbG90Q291bnRzKGRkcywiRU5TRzAwMDAwMTU4MjU4IixpbnRncm91cCA9ICJUcmVhdGVkIikKYGBgCgpgYGB7cn0KIyMgdGhpcyBnZW5lIGhhcyBhIG5lZ2F0aXZlIGZvbGQtY2hhbmdlCnBsb3RDb3VudHMoZGRzLCJFTlNHMDAwMDAxMzY5OTkiLGludGdyb3VwID0gIlRyZWF0ZWQiKQpgYGAKCkZyb20gdGhlIHBsb3RzIHdlIGNhbiBjb25maXJtIHRoYXQgdW4tdHJlYXRlZCBzYW1wbGVzIChhbm5vdGF0ZWQgd2l0aCBgTmAgaW4gdGhlIGBUcmVhdG1lbnRgIGdyb3VwKSB3YXMgdXNlZCBhcyBhIGJhc2VsaW5lOyBpbiBvdGhlciB3b3JkcyBhIHBvc2l0aXZlIGZvbGQtY2hhbmdlIG1lYW5zIGEgaGlnaGVyIGV4cHJlc3Npb24gbGV2ZWwgaW4gVHJlYXRlZCBzYW1wbGVzLiBXZSB3aWxsIHNlZSBzaG9ydGx5IGhvdyB0byBjaGFuZ2UgdGhlIGRpcmVjdGlvbiBvZiB0aGUgY29udHJhc3QuCgo8ZGl2IGNsYXNzPSJpbmZvcm1hdGlvbiI+CklmIHlvdXIgc3R1ZHkgaW52b2x2ZXMga25vY2tpbmctb3V0IGEgcGFydGljdWxhciBnZW5lLCBvciB5b3UgaGF2ZSBzb21lIHBvc2l0aXZlIGNvbnRyb2xzIHRoYXQgYXJlIGtub3duIGluIGFkdmFuY2UsIGl0IHdvdWxkIGJlIGEgZ29vZCBpZGVhIHRvIHZpc3VhbGlzZSB0aGVpciBleHByZXNzaW9uIGxldmVsIHdpdGggYHBsb3RDb3VudHNgLgo8L2Rpdj4KCiMgRXhlcmNpc2UKCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KLSBSZS1ydW4gdGhlIGBERVNlcWAgZnVuY3Rpb24gdG8gZmluZCBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQgZ2VuZXMgYmV0d2VlbiB0aGUgYFRHRmAgdHJlYXRlZCBzYW1wbGVzIGFuZCBgQ1RSTGAuIFlvdSB3aWxsIG5lZWQgdG8gY2hhbmdlIHRoZSBgZGVzaWduYCB0aGF0IGlzIHVzZWQuCi0gV3JpdGUgYSBjc3YgZmlsZSB0aGF0IGNvbnRhaW5zIHJlc3VsdHMgZm9yIHRoZSBnZW5lcyB0aGF0IGhhdmUgYSBwLXZhbHVlIGxlc3MgdGhhbiAwLjA1IGFuZCBhIGxvZzIgZm9sZCBjaGFuZ2UgbW9yZSB0aGFuIDEsIG9yIGxlc3MgdGhhbiAtMS4KICAtIEhJTlQ6IFNvIHRoYXQgd2UgZG9uJ3Qgb3ZlcndyaXRlIG91ciByZXN1bHRzIHNvIGZhciwgaXQgbWF5IGJlIGNvbnZlbmllbnQgdG8gY3JlYXRlIGEgbmV3IGBERVNlcURhdGFTZXRgIG9iamVjdCBmb3IgdGhlIG5ldyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4gQ2hlY2sgdGhlIGBjb2xEYXRhYCB0byBzZWUgd2hpY2ggYW5hbHlzZXMgY2FuIGJlIG1hZGUKLSBVc2UgdGhlIGBwbG90Q291bnRzYCBmdW5jdGlvbiB0byB2aXN1YWxseS1pbnNwZWN0IHRoZSBtb3N0IHN0YXRpc3RpY2FsbHktc2lnbmlmaWNhbnQgZ2VuZSBpZGVudGlmaWVkCgpgYGB7ciBldmFsPUZBTFNFfQoKZGRzX2NvbmRpdGlvbjwtIGRkcwpjb2xEYXRhKGRkc19jb25kaXRpb24pCiMjIFlvdSB3aWxsIG5lZWQgdG8gY2hhbmdlIHRoaXMgbGluZSB0byBjaG9vc2UgdGhlIGNvcnJlY3QgY29tcGFyaXNvbgpkZXNpZ24oZGRzX2NvbmRpdGlvbikgPC0gfi4uLgpgYGAKCjwvZGl2PgoKCgojIENoYW5naW5nIHRoZSBkaXJlY3Rpb24gb2YgdGhlIGNvbnRyYXN0CgpJbiB0aGlzIGluaXRpYWwgYW5hbHlzaXMgYERFU2VxMmAgaGFzIGF1dG9tYXRpY2FsbHkgZGVjaWRlZCB3aGljaCBtZW1iZXIgb2Ygb3VyIHNhbXBsZSBncm91cHMgdG8gdXNlIGFzIG91ciBiYXNlbGluZSAoYENUUmAgaW4gdGhpcyBjYXNlKSBzbyB0aGF0IHRoZSBsb2cyIGZvbGQgY2hhbmdlcyBhcmUgcmVwb3J0ZWQgd2l0aCBhIHBvc2l0aXZlIHZhbHVlIG1lYW5pbmcgaGlnaGVyIGV4cHJlc3Npb24gaW4gVHJlYXRlZC4gSWYgd2Ugd2FudCB0byBjaGFuZ2UgdGhpcyBiZWhhdmlvdXIgd2UgY2FuIGNoYW5nZSB0aGUgYGNvbnRyYXN0YCBhcmd1bWVudCBpbiB0aGUgYHJlc3VsdHNgIGZ1bmN0aW9uCgoKYGBge3IgZXZhbD1GQUxTRX0KIyMgVGhpcyBzaG91bGQgZ2l2ZSB0aGUgc2FtZSBhcyB0aGUgdGFibGUgYWJvdmUKcmVzdWx0cyhkZV90cmVhdGVkLCBjb250cmFzdD1jKCJUcmVhdGVkIiwiWSIsIk4iKSkKIyMgQ2hhbmdpbmcgdGhlIGRpcmVjdGlvbiBvZiB0aGUgY29udHJhc3QKcmVzdWx0cyhkZV90cmVhdGVkLCBjb250cmFzdD1jKCJUcmVhdGVkIiwiTiIsIlkiKSkKYGBgCgojIEFuYWx5c2VzIGludm9sdmluZyBtb3JlIHRoYW4gdHdvIGdyb3VwcwoKSWYgd2UgY2hhbmdlIHRvIHBlcmZvcm1pbmcgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgb24gdGhlIGBjb25kaXRpb25gIHZhcmlhYmxlIHRoZW4gdGhlcmUgYXJlIHZhcmlvdXMgY29udHJhc3RzIHRoYXQgY2FuIGJlIG1hZGU7IGBJUmAgdnMgYENUUmAsIGBUR0ZgIHZzIGBDVFJgIGV0Yy4gV2hlbiB0aGUgYHJlc3VsdHNgIGZ1bmN0aW9uIGlzIHJ1biwgdGhlIHRhYmxlIHRoYXQgaXMgZGlzcGxheWVkIGlzIGZvciB0aGUgY29udHJhc3QgYFRHRiB2cyBDVFJgLiBUaGUgYHJlc3VsdHNOYW1lc2AgZnVuY3Rpb24gY2FuIHRlbGwgdXMgd2hpY2ggb3RoZXIgY29udHJhc3RzIHdlIGNhbiBhY2Nlc3MuCgpgYGB7cn0KZGRzX2NvbmRpdGlvbjwtIGRkcwpkZHNfY29uZGl0aW9uJGNvbmRpdGlvbiA8LSBhcy5mYWN0b3Ioc3RyaW5ncjo6c3RyX3RvX3VwcGVyKGRkc19jb25kaXRpb24kY29uZGl0aW9uKSkKZGVzaWduKGRkc19jb25kaXRpb24pIDwtIH5jb25kaXRpb24KZGVfY29uZGl0aW9uIDwtIERFU2VxKGRkc19jb25kaXRpb24pCnJlc3VsdHNOYW1lcyhkZV9jb25kaXRpb24pCmBgYApGb3IgYSBmYWN0b3Igd2l0aCBtb3JlIHRoYW4gMiBncm91cHMsIHRoZSBgY29udHJhc3RgIGFyZ3VtZW50IGNhbiBiZSB1c2VkIHRvIGNoYW5nZSB0aGUgb3V0cHV0LiBIZXJlLCB3ZSBjb21wYXJlIHRoZSBgSVJgIHRvIGBDVFJgIC10cmVhdGVkIGdyb3Vwcy4KCmBgYHtyIH0KcmVzdWx0cyhkZV9jb25kaXRpb24sIGNvbnRyYXN0ID0gYygiY29uZGl0aW9uIiwiSVIiLCJDVFIiKSx0aWR5PVRSVUUpICAlPiUKYXJyYW5nZShwYWRqKSAKYGBgCgpJZiB3ZSB3YW50ZWQgdG8gY2hhbmdlIHRoZSBhbmFseXNpcyB0byBgSVJgIHZlcnN1cyBgVEdGYCB3ZSBkb24ndCBoYXZlIHRvIHJlLXJ1biB0aGUgYW5hbHlzaXMuIFRoZSBgcmVzdWx0c2AgZnVuY3Rpb24gY2FuIGJlIHVzZWQgdG8gZXh0cmFjdCBhIGRpZmZlcmVudCBjb250cmFzdC4KCmBgYHtyfQpyZXN1bHRzKGRlX2NvbmRpdGlvbiwgY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJJUiIsIlRHRiIpLHRpZHk9VFJVRSkgJT4lCmFycmFuZ2UocGFkaikgCmBgYAojIEFuYWx5c2luZyBhIHN1YnNldCBvZiB0aGUgZGF0YQoKVGhlIGB0eGltcG9ydGAgYW5kIGBERVNlcTJgIHBhY2thZ2VzIG1ha2UgaXQgcXVpdGUgY29udmVuaWVudCB0byBpbXBvcnQgYSBzZXQgb2YgUk5BLXNlcSBjb3VudHMgaW50byBSLiBIb3dldmVyLCB3ZSBkb24ndCBuZWNjZXNhcmlseSBoYXZlIHRvIHVzZSBhbGwgdGhlIHNhbXBsZXMgaW4gb3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLgoKRm9yIGV4YW1wbGUsIHdlIG1pZ2h0IGRlY2lkZSwgYmFzZWQgb24gdGhlIHNhbXBsZSBRQywgdGhhdCBvbmUgb2Ygb3VyIHNhbXBsZXMgc2hvdWxkIGJlIHJlbW92ZWQgYXMgYW4gb3V0bGllci4KClRoZSBgZGRzYCBvYmplY3QgY2FuIGJlIHRyZWF0ZWQgd2l0aCB0aGUgdXN1YWwgc3Vic2V0dGluZyBjb252ZW50aW9ucyBhcyBhIGNvdW50IG1hdHJpeCAoZXZlbiB0aG91Z2ggaXQgaXMgYSBtb3JlIGNvbXBsZXggZGF0YSB0eXBlKS4gU3BlY2lmaWNhbGx5IHdlIGNhbiBkZWZpbmUgYSBzZXQgb2YgY29sdW1uIGluZGljZXMgdG8gc3Vic2V0IHRoZSBzYW1wbGVzLgoKZS5nLiB0aGlzIHdpbGwgcmV0dXJuIGEgYERFU2VxRGF0YXNldGAgdXNpbmcgdGhlIGZpcnN0IGZvdXIgc2FtcGxlcyBmcm9tIGBkZHNgLgoKYGBge3J9CmRkc1ssMTo0XQpgYGAKCgpgYGB7ciBldmFsPUZBTFNFfQpzYW1wbGVzX3RvX2tlZXAgPC0gYygiMV9DVFJfQkNfMiIsIjJfVEdGX0JDXzQiLCIzX0lSX0JDXzUiLAogICAgICAgICAgICAgICAgICAgICAiNF9DVFJfQkNfNiIsIjVfVEdGX0JDXzciLCAiNl9JUl9CQ18xMiIsCiAgICAgICAgICAgICAgICAgICAgICI3X0NUUl9CQ18xMyIsIjhfVEdGX0JDXzE0IiwiOV9JUl9CQ18xNSIpCmRkc19maW5hbCA8LSBkZHNbLGRkcyRSdW4gJWluJSBzYW1wbGVzX3RvX2tlZXBdCiMjIFByb2NlZWQgYXMgdXN1YWwKYGBgCgoKYGBge3IgZXZhbD1GQUxTRX0Kc2FtcGxlc190b19yZW1vdmUgPC0gIjdfQ1RSX0JDXzEzIgpkZHNfZmluYWwgPC0gZGRzWywhZGRzJFJ1biAlaW4lIHNhbXBsZXNfdG9fcmVtb3ZlXQoKYGBgCgpPciBiYXNlZCBvbiBzb21lIGNyaXRlcmlhIHN1Y2ggYXMgdGhlIG51bWJlciBvZiByZWFkcwoKYGBge3J9Cm51bWJlcl9vZl9yZWFkcyA8LSBjb2xTdW1zKGNvdW50cyhkZHMpKSAvIDFlNgpkZHNfZmluYWwgPC0gZGRzWyxudW1iZXJfb2ZfcmVhZHMgPiAzNV0KYGBgCkZpbmFsbHksIHdlIG1pZ2h0IGRlY2lkZSB0byBhbmFseXNlIGEgc3BlY2lmaWMgYmlvbG9naWNhbCBncm91cC4KCmBgYHtyfQpkZHNfdHJlYXRlZCA8LSBkZHNbLGRkcyRUcmVhdGVkID09ICJZIl0KYGBgCgojIE1vcmUtY29tcGxleCBkZXNpZ25zCgpUaGUgZXhhbXBsZXMgd2UgaGF2ZSB1c2VkIHNvIGZhciBoYXZlIHBlcmZvcm1lZCBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHVzaW5nIGEgbmFtZWQgY29sdW1uIGluIHRoZSBgY29sRGF0YWAgb2JqZWN0LiBUaGUgYERFU2VxMmAgcGFja2FnZSBpcyBjYXBhYmxlIG9mIHBlcmZvcm1pbmcgbW9yZSBjb21wbGV4IGFuYWx5c2VzIHRoYXQgY2FuIHRha2UgbXVsdGlwbGUgZmFjdG9ycyBpbnRvIGNvbnNpZGVyYXRpb24gYXQgdGhlIHNhbWUgdGltZTsgc28tY2FsbGVkICJtdWx0aS1mYWN0b3IgZGVzaWducyIKCi0gW011bHRpLWZhY3RvciBkZXNpZ25zIGluIERFU2VxMl0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI211bHRpLWZhY3Rvci1kZXNpZ25zKQoKVGhlIHVzZSBvZiBzdWNoIGEgZGVzaWduIGNvdWxkIGJlIG1vdGl2YXRlZCBieSBkaXNjb3ZlcmluZyBzb3VyY2VzIG9mIHRlY2huaWNhbCB2YXJpYXRpb24gaW4gb3VyIGRhdGEgdGhhdCBtaWdodCBvYnNjdXJlIHRoZSBiaW9sb2dpY2FsIGRpZmZlcmVuY2VzIHdlIHdvdWxkIGxpa2UgdG8gY29tcGFyZS4gZS5nLgoKIVtdKGltYWdlcy9iYXRjaF9lZmZlY3QucG5nKQoKSW4gdGhlIGV4YW1wbGUgaW1hZ2UgYWJvdmUgdGhlIG1haW4gc291cmNlIG9mIHZhcmlhdGlvbiBpcyB0aGUgYmF0Y2ggaW4gd2hpY2ggdGhlIHNhbXBsZXMgd2VyZSBzZXF1ZW5jZWQuIEEgbXVsdGktZmFjdG9yIGFuYWx5c2lzIHRvIGNvbXBhcmUgdGhlIHZhcmlvdXMgY29uZGl0aW9ucywgYnV0ICJjb3JyZWN0IiBmb3IgZGlmZmVyZW5jZXMgaW4gYmF0Y2gsIHdvdWxkIGJlIGFzIGZvbGxvd3MuCgpgYGB7ciBldmFsPUZBTFNFfQojIyMgRG9uJ3QgcnVuIHRoaXMuIEl0J3MganVzdCBhIGNvZGUgZXhhbXBsZQpkZXNpZ24oTVlfREFUQSkgPC0gfiBiYXRjaCArIGNvbmRpdGlvbgpgYGAKCkxpa2V3aXNlLCBpZiB3ZSBoYXZlIGRpZmZlcmVudCB0cmVhdG1lbnRzIGFwcGxpZWQgdG8gZGlmZmVyZW5jZSBjZWxsLWxpbmVzLCBidXQgdGhlIG1haW4gc291cmNlIG9mIHZhcmlhdGlvbiBpcyB0aGUgY2VsbCBsaW5lIChhcyBpbiBXZWVrbHkgRXhlcmNpc2UgNCksIHRoZSBmb2xsb3dpbmcgY291bGQgYmUgdXNlZC4KCmBgYHtyIGV2YWw9RkFMU0V9CiMjIyBEb24ndCBydW4gdGhpcy4gSXQncyBqdXN0IGEgY29kZSBleGFtcGxlCmRlc2lnbihNWV9EQVRBKSA8LSB+Y2VsbF9saW5lICsgdHJlYXRtZW50CmBgYAoKIyBBZGRpbmcgYW5ub3RhdGlvbiB0byB0aGUgREVTZXEyIHJlc3VsdHMKCldlIHdvdWxkIGxvdmUgdG8gc2hhcmUgdGhpcyBsaXN0IHdpdGggb3VyIGNvbGxhYm9yYXRvcnMsIG9yIHNlYXJjaCBmb3Igb3VyIGZhdm91cml0ZSBnZW5lIGluIHRoZSByZXN1bHRzLiBIb3dldmVyLCB0aGUgcmVzdWx0cyBhcmUgbm90IHZlcnkgdXNlZnVsIGluIHRoZXJlIGN1cnJlbnQgZm9ybSBhcyBlYWNoIHJvdyBpcyBuYW1lZCBhY2NvcmRpbmcgdG8gYW4gKkVuc2VtYmwqIGlkZW50aWZpZXIuIFdoaWxzdCBnZW5lIHN5bWJvbHMgYXJlIHByb2JsZW1hdGljIGFuZCBjYW4gY2hhbmdlIG92ZXIgdGltZSwgdGhleSBhcmUgdGhlIG5hbWVzIHRoYXQgYXJlIG1vc3QgcmVjb2duaXNhYmxlIGFuZCBtYWtlIHRoZSByZXN1bHRzIGVhc2llciB0byBuYXZpZ2F0ZS4KCgoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIHdheXMgdG8gYWRkIGFubm90YXRpb24sIGJ1dCB3ZSB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0byBkbyB0aGlzIHVzaW5nIHRoZSAqb3JnLkhzLmVnLmRiKiBwYWNrYWdlLiBUaGlzIHBhY2thZ2UgaXMgb25lIG9mIHNldmVyYWwgKm9yZ2FuaXNtLWxldmVsKiBwYWNrYWdlcyBpbiBCaW9jb25kdWN0b3IgdGhhdCBhcmUgcmUtYnVpbHQgZXZlcnkgNiBtb250aHMuIFRoZXNlIHBhY2thZ2VzIGFyZSBsaXN0ZWQgb24gdGhlIFthbm5vdGF0aW9uIHNlY3Rpb25dKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvQmlvY1ZpZXdzLmh0bWwjX19fQW5ub3RhdGlvbkRhdGEpIG9mIHRoZSBCaW9jb25kdWN0b3IsIGFuZCBhcmUgaW5zdGFsbGVkIGluIHRoZSBzYW1lIHdheSBhcyByZWd1bGFyIEJpb2NvbmR1Y3RvciBwYWNrYWdlcy4gQW4gYWx0ZXJuYXRpdmUgYXBwcm9hY2ggaXMgdG8gdXNlIGBiaW9tYVJ0YCwgYW4gaW50ZXJmYWNlIHRvIHRoZSBbQmlvTWFydF0oaHR0cDovL3d3dy5iaW9tYXJ0Lm9yZy8pIHJlc291cmNlLiBCaW9NYXJ0IGlzIG11Y2ggbW9yZSBjb21wcmVoZW5zaXZlLCBidXQgdGhlIG9yZ2FuaXNtIHBhY2thZ2VzIGZpdCBiZXR0ZXIgaW50byB0aGUgQmlvY29uZHVjdG9yIHdvcmtmbG93LgoKCmBgYHtyIGV2YWw9RkFMU0V9CiMjIyBPbmx5IGV4ZWN1dGUgd2hlbiB5b3UgbmVlZCB0byBpbnN0YWxsIHRoZSBwYWNrYWdlCmluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKQmlvY01hbmFnZXI6Omluc3RhbGwoIm9yZy5Icy5lZy5kYiIpCiMgRm9yIEh1bWFuCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJvcmcuSHMuZWcuZGIiKQpgYGAKClRoZSBwYWNrYWdlcyBhcmUgbGFyZ2VyIGluIHNpemUgdGhhdCBCaW9jb25kdWN0b3Igc29mdHdhcmUgcGFjYWtnZXMsIGJ1dCBlc3NlbnRpYWxseSB0aGV5IGFyZSBkYXRhYmFzZXMgdGhhdCBjYW4gYmUgdXNlZCB0byBtYWtlICpvZmZsaW5lKiBxdWVyaWVzLiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkob3JnLkhzLmVnLmRiKQpgYGAKCgo8ZGl2IGNsYXNzPSJpbmZvcm1hdGlvbiI+ClRoZSBmb2xsb3dpbmcgZXJyb3IgbWF5IGJlIHNlZW4gbG9hZGluZyB0aGlzIGxpYnJhcnkgb24gc29tZSB2ZXJzaW9ucyBvZiBSU3R1ZGlvLiAKCmBgYApTaG93IGluIE5ldyBXaW5kb3dDbGVhciBPdXRwdXRFeHBhbmQvQ29sbGFwc2UgT3V0cHV0CkVycm9yOiBwYWNrYWdlIG9yIG5hbWVzcGFjZSBsb2FkIGZhaWxlZCBmb3Ig4oCYb3JnLkhzLmVnLmRi4oCZOiAub25Mb2FkIGZhaWxlZCBpbiBsb2FkTmFtZXNwYWNlKCkgZm9yICdvcmcuSHMuZWcuZGInLCBkZXRhaWxzOiBjYWxsOiBsJGNvbnRhaW5zIGVycm9yOiAkIG9wZXJhdG9yIGlzIGludmFsaWQgZm9yIGF0b21pYyB2ZWN0b3JzCmBgYAoKQSBzb2x1dGlvbiBpcyBwcmVzZW50ZWQgaW4gdGhpcyBbZm9ydW0gcG9zdF0oaHR0cHM6Ly9jb21tdW5pdHkucnN0dWRpby5jb20vdC9wcm9ibGVtLWluLWxvYWRpbmctbmFtZXNwYWNlLW9mLW9yZy1ocy1lZy1kYi1wYWNrYWdlLWluLXJzdHVkaW8tc2VydmVyLWVycm9yLW9wZXJhdG9yLWlzLWludmFsaWQtZm9yLWF0b21pYy12ZWN0b3JzLzEwMTc4OSkKCgpgYGB7ciBldmFsPUZBTFNFfQpvcHRpb25zKGNvbm5lY3Rpb25PYnNlcnZlciA9IE5VTEwpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpgYGAKCgo8L2Rpdj4KCgpGaXJzdCB3ZSBuZWVkIHRvIGRlY2lkZSB3aGF0IGluZm9ybWF0aW9uIHdlIHdhbnQuIEluIG9yZGVyIHRvIHNlZSB3aGF0IHdlIGNhbiBleHRyYWN0IHdlIGNhbiBydW4gdGhlIGBjb2x1bW5zYCBmdW5jdGlvbiBvbiB0aGUgYW5ub3RhdGlvbiBkYXRhYmFzZS4KCmBgYHtyfQpjb2x1bW5zKG9yZy5Icy5lZy5kYikKYGBgCgpXZSBhcmUgZ29pbmcgdG8gZmlsdGVyIHRoZSBkYXRhYmFzZSBieSBhIGtleSBvciBzZXQgb2Yga2V5cyBpbiBvcmRlciB0byBleHRyYWN0IHRoZSBpbmZvcm1hdGlvbiB3ZSB3YW50LiBWYWxpZCBuYW1lcyBmb3IgdGhlIGtleSBjYW4gYmUgcmV0cmlldmVkIHdpdGggdGhlIGBrZXl0eXBlc2AgZnVuY3Rpb24uCgpgYGB7cn0Ka2V5dHlwZXMob3JnLkhzLmVnLmRiKQpgYGAKCldlIHNob3VsZCBzZWUgYEVOU0VNQkxgLCB3aGljaCBpcyB0aGUgdHlwZSBvZiBrZXkgd2UgYXJlIGdvaW5nIHRvIHVzZSBpbiB0aGlzIGNhc2UuIElmIHdlIGFyZSB1bnN1cmUgd2hhdCB2YWx1ZXMgYXJlIGFjY2VwdGFibGUgZm9yIHRoZSBrZXksIHdlIGNhbiBjaGVjayB3aGF0IGtleXMgYXJlIHZhbGlkIHdpdGggYGtleXNgCgpgYGB7cn0Ka2V5cyhvcmcuSHMuZWcuZGIsIGtleXR5cGU9IkVOU0VNQkwiKVsxOjEwXQpgYGAKCgoKRm9yIHRoZSB0b3AgZ2VuZSBpbiBvdXIgYW5hbHlzaXMgdGhlIGNhbGwgdG8gdGhlIGZ1bmN0aW9uIHdvdWxkIGJlOi0KCmBgYHtyIGV2YWw9RkFMU0V9CnNlbGVjdChvcmcuSHMuZWcuZGIsIGtleXM9IkVOU0cwMDAwMDE1ODI1OCIsCiAgICAgICBrZXl0eXBlID0gIkVOU0VNQkwiLGNvbHVtbnM9YygiU1lNQk9MIiwiR0VORU5BTUUiKQopCmBgYAoKVW5mb3J0dW5hdGVseSwgdGhlIGF1dGhvcnMgb2YgYGRwbHlyYCBhbmQgYEFubm90YXRpb25EYmlgIGhhdmUgYm90aCBkZWNpZGVkIHRvIHVzZSB0aGUgbmFtZSBgc2VsZWN0YCBpbiB0aGVpciBwYWNrYWdlcy4gVG8gYXZvaWQgY29uZnVzaW9uIGFuZCBlcnJvcnMsIHRoZSBmb2xsb3dpbmcgY29kZSBpcyBzb21ldGltZXMgdXNlZCB0byBleHBsaWN0bHkgdGVsbCBSU3R1ZGlvIHRvIHVzZSB0aGUgYHNlbGVjdGAgZnVuY3Rpb24gZnJvbSBgQW5ub3RhdGlvbkRiaWAuCgpgYGB7cn0KQW5ub3RhdGlvbkRiaTo6c2VsZWN0KG9yZy5Icy5lZy5kYiwga2V5cz0iRU5TRzAwMDAwMTU4MjU4IixrZXl0eXBlID0gIkVOU0VNQkwiLGNvbHVtbnM9YygiU1lNQk9MIiwiR0VORU5BTUUiKSkKYGBgCgoKVG8gYW5ub3RhdGUgb3VyIHJlc3VsdHMsIHdlIGRlZmluaXRlbHkgd2FudCBnZW5lIHN5bWJvbHMgYW5kIHBlcmhhcHMgdGhlIGZ1bGwgZ2VuZSBuYW1lLiBMZXQncyBidWlsZCB1cCBvdXIgYW5ub3RhdGlvbiBpbmZvcm1hdGlvbiBpbnRvIGEgbmV3IGRhdGEgZnJhbWUgdXNpbmcgdGhlIGBzZWxlY3RgIGZ1bmN0aW9uLiBUaGUgYGtleXNgIGFyZ3VtZW50IHRvIGBzZWxlY3RgIGlzIGEgKnZlY3Rvciogb2YgdmFsaWQgaWRlbnRpZmllcnMuIFRoZSBuYW1lcyBvZiBhbGwgRW5zZW1ibCBJRHMgdXNlZCBpbiB0aGUgYW5hbHlzaXMgY2FuIGJlIGZvdW5kIGFzIHRoZSBgcm93bmFtZXNgIG9mIG91ciBgREVTZXEyYCBvYmplY3QuIAoKYGBge3J9CiMjIEdldCB0aGUgbmFtZXMgb2YgYWxsIGdlbmVzIHVzZWQgaW4gdGhlIGFuYWx5c2lzCmFsbF9pZHMgPC0gcm93bmFtZXMoZGRzKQphbm5vIDwtIEFubm90YXRpb25EYmk6OnNlbGVjdChvcmcuSHMuZWcuZGIsa2V5cz1hbGxfaWRzLAogICAgICAgICAgICAgIGNvbHVtbnM9YygiU1lNQk9MIiwiR0VORU5BTUUiKSwKICAgICAgICAgICAgICBrZXl0eXBlPSJFTlNFTUJMIikKIyBIYXZlIGEgbG9vayBhdCB0aGUgYW5ub3RhdGlvbgpoZWFkKGFubm8pCmBgYAoKSG93ZXZlciwgd2UgaGF2ZSBhIHByb2JsZW0gdGhhdCB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgaGFzIG1vcmUgcm93cyB0aGFuIG91ciByZXN1bHRzIHRhYmxlLiBUaGlzIGlzIGR1ZSB0byB0aGUgKm9uZS10by1tYW55KiByZWxhdGlvbnNoaXBzIHRoYXQgb2Z0ZW4gb2NjdXIgd2hlbiBtYXBwaW5nIGJldHdlZW4gdmFyaW91cyBpZGVudGlmaWVycy4KCmBgYHtyfQpkaW0oYW5ubykKZGltKHJlc3VsdHNfdHJlYXRlZCkKYGBgCgpTdWNoIGR1cGxpY2F0ZWQgZW50cmllcyBjYW4gYmUgaWRlbnRpZmllZCB1c2luZyB0aGUgYGR1cGxpY2F0ZWRgIGZ1bmN0aW9uLiBUaGlzIHdpbGwgYmUgZWl0aGVyIGBUUlVFYCBvciBgRkFMU0VgIGlmIGEgcGFydGljdWxhciBlbnRyeSBpcyByZXBlYXRlZCBhZ2FpbiBpbiBhIGNvbHVtbi4gU2luY2Ugd2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIGVudHJpZXMgdGhhdCBhcmUgKm5vdCogcmVwZWF0ZWQgd2UgYW4gdXNlIHRoZSBSIHN5bnRheCBgIWAgd2hpY2ggd2lsbCByZXZlcnNlIGEgc2V0IG9mIGxvZ2ljYWwgdmFsdWVzLgoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoeCA9IDE6MTAsIHkgPSBjKExFVFRFUlNbMTo2XSxMRVRURVJTWzE6NF0pKQpkZgpgYGAKCmBgYHtyfQpmaWx0ZXIoZGYsIGR1cGxpY2F0ZWQoeSkpCmBgYAoKYGBge3J9CmZpbHRlcihkZiwgIWR1cGxpY2F0ZWQoeSkpCmBgYAoKCkZvcnR1bmF0ZWx5LCB0aGVyZSBhcmUgbm90IHRvbyBtYW55IGR1cGxpY2F0ZWQgZW50cmllcyBzbyBob3BlZnVsbHkgd2Ugd29uJ3QgbG9zZSB0b28gbXVjaCBpbmZvcm1hdGlvbiBpZiB3ZSBkaXNjYXJkIHRoZSBlbnRyaWVzIHRoYXQgYXJlIGR1cGxpY2F0ZXMgVGhlIGZpcnN0IG9jY3VycmVuY2Ugb2YgdGhlIGR1cGxpY2F0ZWQgSUQgd2lsbCBzdGlsbCBiZSBpbmNsdWRlZCBpbiB0aGUgdGFibGUuCgpgYGB7cn0KYW5ubyA8LSBBbm5vdGF0aW9uRGJpOjpzZWxlY3Qob3JnLkhzLmVnLmRiLGtleXM9YWxsX2lkcywKICAgICAgICAgICAgY29sdW1ucz1jKCJFTlNFTUJMIiwiU1lNQk9MIiwiR0VORU5BTUUiLCJFTlRSRVpJRCIpLAogICAgICAgICAgICBrZXl0eXBlPSJFTlNFTUJMIikgJT4lIApmaWx0ZXIoIWR1cGxpY2F0ZWQoRU5TRU1CTCkpCmRpbShhbm5vKQpgYGAKCgpXZSBjYW4gbm93IGpvaW4gdGhlIGFubm90YXRpb24gaW5mb3JtYXRpb24gdG8gdGhlIGByZXN1bHRzYCBkYXRhIGZyYW1lLiBUaGlzIGNhbiBiZSBkb25lIGJlY2F1c2UgYm90aCBkYXRhIGZyYW1lIGNvbnRhaW5zIHRoZSBzYW1lIHNldCBvZiBpZGVudGlmaWVycy4gSG93ZXZlciwgdGhlIGNvbHVtbiBuYW1lcyBhcmUgZGlmZmVyZW50IHNvIG5lZWQgdG8gdGVsbCBSIHdoaWNoIGNvbHVtbnMgY29udGFpbiB0aGUgc2FtZSBkYXRhIChgcm93YCBhbmQgYEVOU0VNQkxgIHJlc3BlY3RpdmVseSkuCgpgYGB7cn0KcmVzdWx0c19hbm5vdGF0ZWQgPC0gbGVmdF9qb2luKHJlc3VsdHNfdHJlYXRlZCwgYW5ubyxieT1jKCJyb3ciPSJFTlNFTUJMIikpIApoZWFkKHJlc3VsdHNfYW5ub3RhdGVkKQpgYGAKCgpXZSBjYW4gc2F2ZSB0aGUgcmVzdWx0cyB0YWJsZSB1c2luZyB0aGUgYHdyaXRlLmNzdmAgZnVuY3Rpb24sIHdoaWNoIHdyaXRlcyB0aGUgcmVzdWx0cyBvdXQgdG8gYSBjc3YgZmlsZSB0aGF0IHlvdSBjYW4gb3BlbiBpbiBleGNlbC4KCmBgYHtyfQpkaXIuY3JlYXRlKCJkZV9hbmFseXNpcyIsc2hvd1dhcm5pbmdzID0gRkFMU0UpCndyaXRlLmNzdihyZXN1bHRzX2Fubm90YXRlZCxmaWxlPSJvdXRfZGF0YS9ERVNlcV90cmVhdG1lbnRfWV92c19OX2Fubm90YXRlZC5jc3YiLHJvdy5uYW1lcz1GQUxTRSkKYGBgCgoKIyBFeGVyY2lzZQoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgoKLSBKb2luIHRoZSBhbm5vdGF0aW9uIHRhYmxlIHRvIHlvdXIgcmVzdWx0cyBmcm9tIHRoZSBERVNlcSBhbmFseXNpcyBvZiBgVEdGYCB2cyBgQ1RSYC4gU2F2ZSB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgYXMgYSBjc3YgZmlsZS4gZS5nLiAoYG91dF9kYXRhL0RFU2VxX2NvbmRpdGlvbl9UR0ZfdnNfQ1RSX2Fubm90YXRlZC5jc3ZgKQotIFRoZSBwdWJsaWNhdGlvbiBnaXZlcyBleGFtcGxlcyBvZiBgQ09MMUExYCwgYENPTDFBMmAgYW5kIGBDT0wzQTFgIGFzIGdlbmVzIHRoYXQgYXJlICp1cC1yZWd1bGF0ZWQqIGluIFRHRi10cmVhdGVkIHNhbXBsZXMgdnMgY29udHJvbHMgKEZpZ3VyZSA2QykuIFVzZSB5b3VyIGRhdGEgdG8gdmVyaWZ5IHRoaXMgYnkgCiAgKyBpKSBwbG90dGluZyB0aGUgY291bnRzIGZvciB0aGVzZSBnZW5lcyAKICArIGlpKSBleHRyYWN0aW5nIHRoZWlyIHAtdmFsdWVzCgoKPC9kaXY+CgpgYGB7cn0KCmBgYAoKCgojIFZpc3VhbGlzYXRpb25zIGluY29ycG9yYXRpbmcgZ2VuZSBuYW1lcwoKIyMgVGhlIHZvbGNhbm8gcGxvdAoKU2ltaWxhciB0byB0aGUgTUEtcGxvdCB3ZSBzYXcgZWFybGllciwgdGhlIHZvbGNhbm8gcGxvdCBpcyBvZnRlbiB1c2VkIHRvIGRpc3BsYXkgdGhlIHJlc3VsdHMgb2YgYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4gVGhlIHgtYXhpcyBzaG93cyB0aGUgbG9nJF8yJCBmb2xkLWNoYW5nZSBhbmQgdGhlIHktYXhpcyBpcyBhIG1lYXN1cmUgb2Ygc2lnbmlmaWNhbmNlIC0gdHlwaWNhbGx5IGEgLWxvZyRfezEwfSQgdHJhbnNmb3JtZWQgdmVyc2lvbiBvZiB0aGUgcC12YWx1ZS4KCiFbXShodHRwczovL3VwbG9hZC53aWtpbWVkaWEub3JnL3dpa2lwZWRpYS9jb21tb25zLzcvNzQvVm9sY2Fub19lZy5qcGcpCgpXZSB3b3VsZCBub3QgYmUgYWJsZSB0byB2aXN1YWxpc2UgdGhlIHAtdmFsdWVzIHdpdGhvdXQgYW55IHRyYW5zZm9ybWF0aW9uICh0aGV5IHdvdWxkIGJlIHNxdWFzaGVkIG9uIHRoZSBib3R0b20gb2YgdGhlIHktYXhpcykuIEJ5IHVzaW5nIGEgbG9nJF97MTB9JCB0cmFuc2Zvcm1hdGlvbiB3ZSBnZXQgcXVhbnRpdGllcyB0aGF0IGFyZSBlYXNpZXIgdG8gdmlzdWFsaXNlLiBBcHBseWluZyBhIG1pbnVzIHNpZ24gdG8gdGhlIHJlc3VsdCBwbGFjZXMgbW9yZSBzaWduaWZpY2FudCBwLXZhbHVlcyBhdCB0aGUgdG9wIG9mIHRoZSBwbG90LgoKYGBge3J9CnAgPC0gYygwLjA1LDAuMDEsMC4wMDAxKQpsb2cxMChwKQotbG9nMTAocCkKYGBgCgoKQWxsIHRoZSBkYXRhIHdlIG5lZWQgZm9yIHRoZSBwbG90IGlzIGluY2x1ZGVkIGluIG91ciBhbm5vdGF0ZWQgcmVzdWx0cyB0YWJsZS4KCmBgYHtyfQojIyBXZSBtYW51YWxseSBzZXQgdGhlIHgtbGltaXQgaW4gdGhpcyBjYXNlCmdncGxvdChyZXN1bHRzX2Fubm90YXRlZCwgYWVzKHggPSBsb2cyRm9sZENoYW5nZSwgeSA9IC1sb2cxMChwYWRqKSkpICsgZ2VvbV9wb2ludCgpICsgeGxpbSgtNSw1KQpgYGAKClRvIG1ha2UgdGhlIHBsb3QgbW9yZSB1c2VmdWwgd2UgY2FuIGxhYmVsIGluZGl2aWR1YWwgcG9pbnRzLiBUaGUgZ2VuZXJhbCB3YXkgb2YgYWRkaW5nIG9mIGFkZGluZyBsYWJlbHMgaXMgYnkgdXNpbmcgYGdlb21fdGV4dGAgaW4gY29uanVuY3Rpb24gd2l0aCB0aGUgYGxhYmVsYCBhZXN0aGV0aWMuIEhvd2V2ZXIsIGl0IGlzIG5vdCB2ZXJ5IHNlbnNpYmxlIHRvIGJsaW5kbHkgYWRkIHRoZSBgZ2VvbV90ZXh0YCBmdW5jdGlvbi4gV2UgdXNlIHRoZSBgU1lNQk9MYCBjb2x1bW4gZnJvbSB0aGUgcmVzdWx0cyB0YWJsZSBhcyB0aGVzZSBhcmUgcmVjb2duaXNhYmxlIGdlbmUgbmFtZXMuCgpgYGB7cn0KZ2dwbG90KHJlc3VsdHNfYW5ub3RhdGVkLGFlcyh4ID0gbG9nMkZvbGRDaGFuZ2UsIHkgPSAtbG9nMTAocGFkaiksbGFiZWw9U1lNQk9MKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3RleHQoKQpgYGAKClRoZSBwcm9ibGVtIGlzIHRoYXQgYGdncGxvdGAgdHJpZXMgdG8gbGFiZWwgZXZlcnkgZ2VuZSB0aGF0IHdhcyB0ZXN0ZWQgaW4gdGhlIGFuYWx5c2lzOyB3aGljaCBpcyBjbGVhcmx5IG5vdCB2ZXJ5IHNlbnNpYmxlLgoKTGV0J3Mgc2F5IHRoYXQgd2Ugb25seSB3YW50IHRvIGxhYmVsIHRoZSB0b3AgMjAgbW9zdCBzaWduaWZpY2FudCBnZW5lcy4gVGhlIGxhYmVsIHdlIHdvdWxkIHdhbnQgdG8gZ2l2ZSB0aGVzZSBnZW5lcyBpcyB0aGUgZ2VuZSBuYW1lIChgU1lNQk9MYCksIHdoZXJlYXMgdGhlIHJlbWluZGVyIG9mIHRoZSBnZW5lcyBzaG91bGQgaGF2ZSBubyBsYWJlbC4gV2UgY2FuIGFzc2lnbiBhIHNldCBvZiBsYWJlbHMgYmFzZWQgb24gd2hlcmUgdGhlIGdlbmVzIGFyZSByYW5rZWQgaW4gdGhlIGdlbmUgbGlzdCBieSB1c2luZyBhbiBgaWZlbHNlYCBzdGF0ZW1lbnQgaW4gUi4KCldpdGggYGlmZWxzZWAgd2UgY2FuIGFzc2lnbiBsYWJlbHMgYmFzZWQgb24gYSBsb2dpY2FsIGNvbmRpdGlvbi4gSWYgdGhlIGNvbmRpdGlvbiBpcyBgVFJVRWAgdGhlbiBvbmUgbGFiZWwgaXMgYXNzaWduZWQ7IGlmIGBGQUxTRWAgYW4gYWx0ZXJuYXRpdmUgaXMgYXNzaWduZWQuIEluIG91ciBjYXNlIHdlIHdhbnQgdGhlIGxhYmVsIHRvIGJlIHRoZSBnZW5lIHN5bWJvbCBpZiB0aGUgZ2VuZSByYW5rcyBsZXNzIHRoYW4gMjAgaW4uIElmIG5vdCwgdGhlIGxhYmVsIGlzIHRoZSBlbXB0eSBzdHJpbmcuCgpgYGB7cn0KbXV0YXRlKHJlc3VsdHNfYW5ub3RhdGVkLCBSYW5rID0gMTpuKCkpICU+JSAKICBtdXRhdGUoTGFiZWwgPSBpZmVsc2UoUmFuayA8PSAyMCwgU1lNQk9MLCAiIikpCmBgYApXZSBjYW4gbm93IHJlLXJ1biB0aGUgcGxvdCBidXQgdXNpbmcgdGhlIG5ld2x5IGNyZWF0ZWQgbGFiZWxzCgpgYGB7cn0KbXV0YXRlKHJlc3VsdHNfYW5ub3RhdGVkLCBSYW5rID0gMTpuKCkpICU+JSAKICBtdXRhdGUoTGFiZWwgPSBpZmVsc2UoUmFuayA8PSAyMCwgU1lNQk9MLCAiIikpICU+JSAKIyMgbm90aWNlIGhvdyB3ZSBzZXQgdGhlIGxhYmVsIGFyZ3VtZW50IHRvIGJlIExhYmVsIHJhdGhlciB0aGFuIFNZTUJPTApnZ3Bsb3QoYWVzKHggPSBsb2cyRm9sZENoYW5nZSwgeSA9IC1sb2cxMChwYWRqKSxsYWJlbD1MYWJlbCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV90ZXh0KGNvbD0iYmx1ZSIpCmBgYAoKQSBuaWNlciB3YXkgb2YgZGlzcGxheWluZyB0aGUgbmFtZXMgaXMgcHJvdmlkZWQgYnkgdGhlIGBnZ3JlcGVsYCBwYWNrYWdlCgpgYGB7cn0KaWYoIXJlcXVpcmUoZ2dyZXBlbCkpIGluc3RhbGwucGFja2FnZXMoImdncmVwZWwiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGdncmVwZWwpCm11dGF0ZShyZXN1bHRzX2Fubm90YXRlZCwgUmFuayA9IDE6bigpKSAlPiUgCiAgbXV0YXRlKExhYmVsID0gaWZlbHNlKFJhbmsgPD0gMjAsIFNZTUJPTCwgIiIpKSAlPiUgCmdncGxvdChhZXMoeCA9IGxvZzJGb2xkQ2hhbmdlLCB5ID0gLWxvZzEwKHBhZGopLGxhYmVsPUxhYmVsKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3RleHRfcmVwZWwoY29sPSJibHVlIikKYGBgCgoKIyMgTWFraW5nIGEgaGVhdG1hcCBmcm9tIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzCgpBIGhlYXRtYXAgaXMgYSB2ZXJ5IHBvcHVsYXIgbWV0aG9kIGZvciB2aXN1YWxpc2luZyBnZW5lIGV4cHJlc3Npb24gZGF0YXNldC4gSXQgc2hvd3MgdGhlIGV4cHJlc3Npb24gbGV2ZWxzIG9mIGEgc2V0IG9mIGdlbmVzLCBhbmQgaG93IHRob3NlIGdlbmVzIHNlcGFyYXRlIHRoZSBzYW1wbGVzIGluIHRoZSBkYXRhc2V0IGludG8gZ3JvdXBzLiBMaWtlIHRoZSBQQ0EgcGxvdCB3ZSB1c2VkIHByZXZpb3VzbHksIGl0IGlzIGFuIHVuc3VwZXJ2aXNlZCBtZXRob2Qgd2hlcmVieSBrbm93biBiaW9sb2dpY2FsIGdyb3VwcyBhcmUgYWRkZWQgYWZ0ZXIgdGhlIHBsb3QgaGFzIGJlZW4gY3JlYXRlZC4KCkhvd2V2ZXIsIHdlIGRvbid0IHVzZSB0aGUgd2hvbGUgZXhwcmVzc2lvbiBtYXRyaXggdG8gY29uc3RydWN0IHRoZSBoZWF0bWFwOyBhdHRlbXB0aW5nIHRvIGRvIHRoaXMgd2lsbCBxdWlja2x5IGNhdXNlIFIgdG8gY3Jhc2guIE1vcmVvZXZlciwgdGhlIHBsb3Qgd291bGRuJ3QgYmUgcGFydGljdWxhcmx5IGluZm9ybWF0aXZlIGFzIG1vc3QgZ2VuZXMgYXJlIG5vdCBleHBlY3RlZCB0byBjaGFuZ2UgYmV0d2VlbiBiaW9sb2dpY2FsIGNvbmRpdGlvbnMsIG9yIHdvbid0IGV2ZW4gYmUgZXhwcmVzc2VkLgoKV2Ugb2Z0ZW4gdXNlIGhlYXRtYXBzIHRvIHNob3cgYSB2ZXJ5IHNtYWxsIHNldCBvZiBnZW5lcy4gVGhlIG1vc3Qgc3RhdGlzdGljYWxseS1zaWduaWZpY2FudCBnZW5lcyBhcmUgYSBwb3B1bGFyIGNob2ljZSBhcyB0aGV5IHNob3VsZCBkaXNjcmltaW5hdGUgd2VsbCBiZXR3ZWVuIGJpb2xvZ2ljYWwgZ3JvdXBzLgoKVGhlIGZpcnN0IHN0ZXAgaXMgdG8gaWRlbnRpZnkgdGhlICpFbnNlbWJsKiBJRHMgb2YgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgZ2VuZXMKCmBgYHtyfQpkZV9pZHMgPC0gcmVzdWx0c19hbm5vdGF0ZWQgJT4lIAogIGFycmFuZ2UocGFkaikgJT4lIAogIGRwbHlyOjpzbGljZSgxOjEwKSAlPiUgCiAgcHVsbCgicm93IikKZGVfaWRzCmBgYApUaGUgaGVhdG1hcCB0b29sIHdlIGFyZSBnb2luZyB0byB1c2UgcmVxdWlyZXMgYSBjb3VudCBtYXRyaXggYXMgaW5wdXQsIGFuZCBvdXIgY291bnQgbWF0cmljZXMgaGF2ZSBFbnNlbWJsIElEcyBhcyByb3cgaWRlbnRpZmllcnMuIEFzIHdpdGggdGhlIGJveHBsb3RzIGFuZCBQQ0EgcGxvdHMgZnJvbSBiZWZvcmUgdGhpcyB2aXN1YWxpc2F0aW9uIHdvcmtzIGJlc3QgZm9yIHRyYW5zZm9ybWVkIGNvdW50cy4KCmBgYHtyfQpsaWJyYXJ5KHBoZWF0bWFwKQp2c2QgPC0gdnN0KGRkcykKaGVhdG1hcF9kYXRhIDwtIGFzc2F5KHZzZClbZGVfaWRzLF0KcGhlYXRtYXAoaGVhdG1hcF9kYXRhKQpgYGAKCkEgKmRlbmRyb2dyYW0qIGlzIHVzZWQgdG8gc2hvdyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gc2FtcGxlczsgd2hlcmVieSBzYW1wbGVzIHRoYXQgaGF2ZSBhIHNob3J0ICJicmFuY2giIGpvaW5pbmcgdGhlbSBiZWluZyBzaW1pbGFyIHRvIGVhY2ggb3RoZXIuIEhvd2V2ZXIsIHNhbXBsZXMgYmVpbmcgbmV4dCB0byBlYWNoIG90aGVyIG9uIHRoZSBwbG90ICoqZG9lcyBub3QqKiBpbXBseSB0aGF0IHRoZXkgYXJlIGNsb3NlbHkgcmVsYXRlZC4KClRoZSBjb2RlIHRvIGdlbmVyYXRlIHRoZSBoZWF0bWFwIGNhbiBiZSBtb2RpZmllZCB0byBpbmNsdWRlIHVzZWZ1bCBzYW1wbGUgYW5ub3RhdGlvbnMuIFRoZSBtZXRhIGRhdGEgZm9yIHRoaXMgZGF0YXNldCBpcyBzdG9yZWQgaW4gYGRkc2AgYnV0IG11c3QgdW5kZXJnb25lIGEgYml0IG9mIG1vZGlmaWNhdGlvbiBiZWZvcmUgd2UgY2FuIGluY2x1ZGUgaXQuCiAKYGBge3J9CiMjIHRoZSBtZXRhIGRhdGEgbXVzdCBiZSBhIGRhdGEgZnJhbWUKc2FtcF9hbm5vIDwtIGRhdGEuZnJhbWUoY29sRGF0YShkZHMpKQojIyB0aGUgcm93bmFtZXMgbXVzdCBtYXRjaCB0aGUgY29sdW1ucyBvZiB0aGUgZGF0YSBiZWluZyBwbG90dGVkCnJvd25hbWVzKHNhbXBfYW5ubykgPC0gZGRzJFJ1bgpwaGVhdG1hcChoZWF0bWFwX2RhdGEsCiAgICAgICAgIGFubm90YXRpb25fY29sID0gc2FtcF9hbm5vKQpgYGAKCldlIGNhbiBkbyBzb21lIHRpZHlpbmctdXAgYXMgbm90IGFsbCB0aGUgY29sdW1ucyBpbiBvdXIgYW5ub3RhdGlvbiBkYXRhIGZyYW1lIG5lZWQgdG8gYmUgZGlzcGxheWVkCgpgYGB7cn0Kc2FtcF9hbm5vIDwtIGRwbHlyOjpzZWxlY3Qoc2FtcF9hbm5vLCBUcmVhdGVkLCBSZXBsaWNhdGUpICU+JSAKICBtdXRhdGUoUmVwbGljYXRlID0gYXMuZmFjdG9yKFJlcGxpY2F0ZSkpCnBoZWF0bWFwKGhlYXRtYXBfZGF0YSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBzYW1wX2Fubm8pCmBgYAoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgoqKkV4ZXJjaXNlKio6IAoKLSBNYWtlIGFub3RoZXIgaGVhdG1hcCBvZiB0aGUgZGF0YXNldCwgYnV0IHRoaXMgdGltZSB1c2luZyB0aGUgMzAgZ2VuZXMgd2l0aCB0aGUgbGFyZ2VzdCBhYnNvbHV0ZSBmb2xkLWNoYW5nZQotIFRoZSBjZWxscyBpbiB0aGUgaGVhdG1hcCBhcmUgb2Z0ZW4gKnNjYWxlZCogYXQgYSBnZW5lLWxldmVsIGFjY29yZGluZyB0byB0aGUgY2hhbmdlIGZyb20gbWVhbiByYXRoZXIgdGhhbiBhYnNvbHV0ZSB2YWx1ZS4gTG9vayBhdCB0aGUgaGVscCBmb3IgYHBoZWF0bWFwYCB0byBzZWUgaG93IHRvIGNoYW5nZSB0aGUgaGVhdG1hcAotIEl0IHdvdWxkIGJlIG1vcmUgaW5mb3JtYXRpdmUgdG8gaGF2ZSB0aGUgcm93cyBsYWJlbGVkIGFjY29yZGluZyB0byB0aGUgYFNZTUJPTGAgcmF0aGVyIHRoYW4gRW5zZW1ibCBJRC4gTG9vayBhdCB0aGUgaGVscCBmb3IgYHBoZWF0bWFwYDsgd2hpY2ggYXJndW1lbnQgZG8geW91IG5lZWQgdG8gY2hhbmdlPyBDaGFuZ2UgdGhlIGhlYXRtYXAgc28gdGhhdCBgU1lNQk9MYHMgYXJlIHNob3duIGZvciBlYWNoIHJvdy4KCjwvZXhlcmNpc2U+CgojIEFQUEVORElYOiBGdWxsIERFU2VxIHdvcmtmbG93CgpUaGUgbWVkaWFuIG9mIHJhdGlvcyBub3JtYWxpc2F0aW9uIG1ldGhvZCBpcyBlbXBsb3llZCBpbiBERVNlcTIgdG8gYWNjb3VudCBmb3IgKnNlcXVlbmNpbmcgZGVwdGgqIGFuZCAqUk5BIGNvbXBvc2l0aW9uKi4gTGV0J3MgZ28gdGhyb3VnaCBhIHNob3J0IHdvcmtlZCBleGFtcGxlIChjb3VydGVzeSBvZiBbaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDJfREdFX2NvdW50X25vcm1hbGl6YXRpb24uaHRtbF0oaGVyZSkpIHRvIGV4cGxhaW4gdGhlIHByb2Nlc3MuCgpgYGB7cn0KIyMgY3JlYXRlIGEgc21hbGwgZXhhbXBsZSBtYXRyaXggb2YgImNvdW50cyIKdGVzdF9kYXRhIDwtIG1hdHJpeChjKDE0ODksMjIsNzkzLDc2LDUyMSw5MDYsMTMsNDEwLDQyLDExOTYpLG5yb3c9NSkKcm93bmFtZXModGVzdF9kYXRhKSA8LSBjKCJFRjJBIiwiQUJDRDEiLCJNRUZWIiwiQkFHMSIsIk1PVjEwIikKY29sbmFtZXModGVzdF9kYXRhKSA8LSBjKCJTYW1wbGVBIiwiU2FtcGxlQiIpCnRlc3RfZGF0YQpgYGAKCkZpcnN0bHksIGFuICJhdmVyYWdlIiBvciByZWZlcmVuY2Ugc2FtcGxlIGlzIGNyZWF0ZWQgdGhhdCByZXByZXNlbnRzIHRoZSBjb3VudHMgb24gYSB0eXBpY2FsIHNhbXBsZSBpbiB0aGUgZGF0YXNldC4gVGhlICpnZW9tZXRyaWMgbWVhbiogaXMgdXNlZCByYXRoZXIgdGhhbiB0aGUgKmFyaXRobWV0aWMqIG1lYW4uIEluIG90aGVyIHdvcmRzIHRoZSBpbmRpdmlkdWFsIGNvdW50cyBhcmUgbXVsdGlwbGllZCByYXRoZXIgdGhhbiBzdUhzZWQgYW5kIHRoZSBtZWFzdXJlIHNob3VsZCBiZSBtb3JlIHJvYnVzdCB0byBvdXRsaWVycy4KCgpgYGB7cn0KcHN1ZWRvX3JlZiA8LSBzcXJ0KHJvd1Byb2RzKHRlc3RfZGF0YSkpCnBzdWVkb19yZWYKYGBgCgpBIHJhdGlvcyBvZiBzYW1wbGUgdG8gInBzdWVkbyByZWZlcmVuY2UiIGFyZSB0aGVuIGNhbGN1bGF0ZWQgZm9yIGVhY2ggZ2VuZS4gV2UgYXJlIGFzc3VtaW5nIHRoYXQgbW9zdCBnZW5lcyBhcmUgbm90IGNoYW5naW5nIGRyYW1hdGljYWxseSwgc28gdGhpcyByYXRpbyBzaG91bGQgYmUgc29tZXdoZXJlIGFyb3VuZCAxLgoKYGBge3J9CnRlc3RfZGF0YS9wc3VlZG9fcmVmCmBgYAoKYERFU2VxMmAgZGVmaW5lcyBzaXplIGZhY3RvcnMgYXMgYmVpbmcgdGhlICptZWRpYW4qIG9mIHRoZXNlIHJhdGlvcyBmb3IgZWFjaCBzYW1wbGUgKG1lZGlhbiBpcyB1c2VkIHNvIGFueSBvdXRsaWVyIGdlbmVzIHdpbGwgbm90IGFmZmVjdCB0aGUgbm9ybWFsaXNhdGlvbikuIAoKYGBge3J9Cm5vcm1fZmFjdG9ycyA8LSBjb2xNZWRpYW5zKHRlc3RfZGF0YS9wc3VlZG9fcmVmKQpub3JtX2ZhY3RvcnMKYGBgCgpJbmRpdmlkdWFsIHNhbXBsZXMgY2FuIHRoZW4gbm9ybWFsaXNlZCBieSBkaXZpZGluZyB0aGUgY291bnQgZm9yIGVhY2ggZ2VuZSBieSB0aGUgY29ycmVzcG9uZGluZyBub3JtYWxpemF0aW9uIGZhY3Rvci4KCmBgYHtyfQp0ZXN0X2RhdGFbLDFdIC8gbm9ybV9mYWN0b3JzWzFdCmBgYAoKYW5kIGZvciB0aGUgc2Vjb25kIHNhbXBsZS4uLgoKYGBge3J9CnRlc3RfZGF0YVssMl0gLyBub3JtX2ZhY3RvcnNbMl0KCmBgYAoKVGhlIHNpemUgZmFjdG9ycyBmb3IgZWFjaCBzYW1wbGUgaW4gb3VyIGRhdGFzZXQgY2FuIGJlIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIGBlc3RpbWF0ZVNpemVGYWN0b3JzRm9yTWF0cml4YCBmdW5jdGlvbi4KCmBgYHtyfQpzZiA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzRm9yTWF0cml4KGFzc2F5KGRkcykpCnNmCmBgYAoKVGhlIGVzdGltYXRpb24gb2YgdGhlc2UgZmFjdG9ycyBjYW4gYWxzbyB0YWtlIGdlbmUtbGVuZ3RocyBpbnRvIGFjY291bnQsIGFuZCB0aGlzIGlzIGltcGxlbWVudGVkIGluIHRoZSBgZXN0aW1hdGVTaXplRmFjdG9yc2AgZnVuY3Rpb24uIEV4dHJhIG5vcm1hbGl6YXRpb24gZmFjdG9yIGRhdGEgaXMgYWRkZWQgdG8gdGhlIGBkZHNgIG9iamVjdC4KCgoKYGBge3IgZXZhbD1GQUxTRX0KZGRzIDwtIGVzdGltYXRlU2l6ZUZhY3RvcnMoZGRzKQpkZHMKYGBgCgpJbiBwcmVwYXJhdGlvbiBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gREVTZXEyIGFsc28gbmVlZCBhIHJlbGlhYmxlIGVzdGltYXRlIG9mIHRoZSB2YXJpYWJpbGl0eSBvZiBlYWNoIGdlbmU7IHdoaWNoIGl0IGNhbGxzICpkaXNwZXJzaW9uKi4gCgpgYGB7ciBldmFsPUZBTFNFfQpkZHMgPC0gZXN0aW1hdGVEaXNwZXJzaW9ucyhkZHMpCmRkcwoKYGBgCgpBIHN0YXRpc3RpY2FsIHRlc3QgY2FuIHRoZW4gYmUgYXBwbGllZC4gQXMgdGhlIGRhdGEgYXJlIGNvdW50LWJhc2VkIGFuZCBub3Qgbm9ybWFsbHktZGlzdHJpYnV0ZWQgYSB0LXRlc3Qgd291bGQgbm90IGJlIGFwcHJvcHJpYXRlLiBNb3N0IHRlc3RzIGFyZSBiYXNlZCBvbiBhICpQb2lzc29uKiBvciAqbmVnYXRpdmUtYmlub21pYWwqIGRpc3RyaWJ1dGlvbjsgbmVnYXRpdmUgYmlub21pYWwgaW4gdGhlIGNhc2Ugb2YgYERFU2VxMmAuIEFsdGhvdWdoIHlvdSBtaWdodCBub3QgYmUgZmFtaWxpYXIgd2l0aCB0aGUgbmVnYXRpdmUgYmlub21pYWwsIHRoZSByZXN1bHRzIHNob3VsZCBiZSBpbiBhIGZhbWlsaWFyIGZvcm0gd2l0aCBmb2xkLWNoYW5nZXMgYW5kIHAtdmFsdWVzIGZvciBlYWNoIGdlbmUuCgpgYGB7ciBldmFsPUZBTFNFfQpkZHMgPC0gbmJpbm9tV2FsZFRlc3QoZGRzKQpgYGAKCkl0IG1heSBzZWVtIGxpa2UgdGhlcmUgaXMgYSBsb3QgdG8gcmVtZW1iZXIsIGJ1dCBmb3J0dW5hdGVseSB0aGVyZSBpcyBvbmUgY29udmVuaWVudCBmdW5jdGlvbiB0aGF0IHdpbGwgYXBwbHkgdGhlIHRocmVlIHN0ZXBzLiBUaGUgbWVzc2FnZXMgcHJpbnRlZCBzZXJ2ZSBhcyByZW1pbmRlcnMgb2YgdGhlIHN0ZXBzIGluY2x1ZGVkLg==